#!/Program Files (x86)/Ruby192/bin/ruby.exe
# runningAhead.rb

require 'date'
require 'digest/md5'
require 'rubygems'
require 'savon'

class RunningAheadError < StandardError
	attr_accessor :canRetry
	
	def initialize(canRetry)
		@canRetry = canRetry
	end
end

# Even I'm not proud of this class code :P
class Workout
	attr_accessor :workoutID, :date, :timeOfDay, :eventType, :eventTypeID, :eventSubType, :eventSubTypeID
	attr_accessor :courseName, :courseID, :distance, :distanceUnit, :duration, :equipmentName, :equipmentID
	attr_accessor :weight, :weightUnit, :restHR, :avgHR, :maxHR, :temperature, :temperatureUnit, :notes
	attr_accessor :weatherConditions, :fieldPlacement, :fieldSize, :groupMinAge, :groupMaxAge, :groupPlacement
	attr_accessor :groupSize, :genderPlacement, :genderSize, :quality, :effort, :intervalSet, :url
	
	def initialize(workoutID = '', date = '', timeOfDay = '', eventType = '', eventTypeID = '',
				   eventSubType = '', eventSubTypeID = '', courseName = '', courseID = '', distance = '',
				   distanceUnit = '', duration = '', equipmentName = '', equipmentID = '', weight = '',
				   weightUnit = '', restHR = '', avgHR = '', maxHR = '', temperature = '', temperatureUnit = '', 
				   notes = '', weatherConditions = '', fieldPlacement = '', fieldSize = '', groupMinAge = '',
				   groupMaxAge = '', groupPlacement = '', groupSize = '', genderPlacement = '', genderSize = '',
				   quality = '', effort = '', intervalSet = '', url = '')
		
		@workoutID, @date, @timeOfDay, @eventType = workoutID, date, timeOfDay, eventType
		@eventTypeID, @eventSubType, @eventSubTypeID = eventTypeID, eventSubType, eventSubTypeID
		@courseName, @courseID, @distance = courseName, courseID, distance
		@distanceUnit, @duration, @equipmentName, @equipmentID = distanceUnit, duration, equipmentName, equipmentID
		@weight, @weightUnit, @restHR, @avgHR, @maxHR = weight, weightUnit, restHR, avgHR, maxHR
		@temperature, @temperatureUnit, @notes = temperature, temperatureUnit, notes
		@weatherConditions, @fieldPlacement, @fieldSize = weatherConditions, fieldPlacement, fieldSize
		@groupMinAge, @groupMaxAge, @groupPlacement = groupMinAge, groupMaxAge, groupPlacement
		@groupSize, @genderPlacement, @genderSize = groupSize, genderPlacement, genderSize
		@quality, @effort, @intervalSet, @url = quality, effort, intervalSet, url
	end
end

class RunningAhead	
	# Class variables
	@@soapAction     = 'http://Api.RunningAHEAD.com'
	
	@@h_connection   = 'Keep-Alive'
	@@h_contentType  = 'application/soap+xml; charset=utf-8; action=' + @@soapAction + '/'
	@@h_host		 = 'api-sandbox.runningahead.com'
	@@h_userAgent    = 'RunningAhead-Ruby-/0.1.0'
	
	@@location       = 'http://' + @@h_host + '/api/api.asmx'
	
	private
	# start private method declarations
		def throwError(num)
			# unused - Porw na kanw kati me to retry statement, an thelw ...
			canRetry = false
			
			case num
				when 10
					message = 'An unknown error has occured.';
				when 11
					message = 'An unknown database error has occured.';
				when 13
					message = 'The requested service is unavailable.';
				when 30
					message = 'The application has not authenticated.';
				when 31
					message = 'The application has insufficient priveleges. (Please contact a runningahead administrator)';
				when 100
					message = 'You have not acquired a session key.';
				when 101
					message = 'Your application is denied access to the API.';
				when 102
					message = 'AppKey is either missing or invalid.';
				when 103
					message = 'Your signature is invalid.';
				when 104
					message = 'Your session key is invalid.';
				when 105
					message = 'The user specified cannot be found.';
				when 106
					message = 'One or more of the parameters is invalid or missing.';
				when 107
					message = 'The data type specified is not supported.';
				when 108
					message = 'The data format is invalid.';
				when 109
					message = 'The data encoding is invalid.';
				when 110
					message = 'The activity type is invalid.';
				when 111
					message = 'The date range is invalid.';
				when 112
					message = 'The data field is missing or invalid.';
				when 113
					message = 'The workout ID is missing or invalid.';
				when 114
					message = 'The date is missing or invalid.';
				when 115
					message = 'The event type ID is missing or invalid';
				when 116
					message = 'The event sub type ID is missing or invalid';
				when 117
					message = 'The interval set name is invalid or missing.';
				when 118
					message = 'The distance is missing or invalid.';
				when 119
					message = 'The distance unit missing or invalid.';
				when 120
					message = 'The weight unit is missing or invalid.';
				when 121
					message = 'The rest, average or max heart rate is invalid.';
				when 122
					message = 'The temperature unit is missing or invalid.';
				when 123
					message = 'The workout effort is missing or invalid.';
				when 124
					message = 'The workout quality is missing or invalid.';
				when 125
					message = 'The interval type is missing or invalid.';
				when 126
					message = 'The interval distance is missing or invalid.';
				when 127
					message = 'The interval distance unit is missing or invalid.';
				when 128
					message = 'The interval heart rate is missing or invalid.';
				when 129
					message = 'The equipment ID is missing or invalid.';
				when 130
					message = 'The brand ID is missing or invalid.';
				when 131
					message = 'The model ID is missing or invalid.';
				when 132
					message = 'The course ID is missing or invalid.';
				when 133
					message = 'The course is missing or invalid.';
				when 134
					message = 'The username is missing or invalid.';
				when 135
					message = 'The specified username already exists.';
				when 136
					message = 'The email is missing or invalid.';
				when 137
					message = 'The specified email already exists.';
				when 138
					message = 'The password is missing or invalid.';
				when 139
					message = 'The temperature unit is missing or invalid.';
				when 140
					message = 'The day of the week is missing or invalid.';
				when 141
					message = 'The username is invalid.';
				when 142
					message = 'The workout indicates it has extended data but the data cannot be located. (Please contact a runningahead administrator)';
				when 143
					message = 'The workout has extended data but the packaging of the data failed. (Please contact a runningahead administrator)';
				when 143
					message = 'The supplied external id is not associated with a user';
				else
					message = 'An unknown error occured.';
			end

			raise RunningAheadError.new(canRetry), "[ERROR] \##{num}: #{@method}: #{message}"
		end

		def setMethod(method)
			@method 	                         = method
			@client.http.headers['Content-Type'] = @@h_contentType + method
		end
		
		def beginSession()
			setMethod 'BeginSession'
								
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = { 'ns1:appKey' => @appKey }
			end
		
			temp = response.to_hash[:begin_session_response][:begin_session_result]
			code = temp[:code].to_i
			if code == 0
				@sessionKey = temp[:data][:session_key]
				return 0
			else
				return code
			end
		rescue Savon::Error => error
			return -1
		end
		
		def authenticateApplication()
			setMethod 'AuthenticateApplication'
								
			signature = @appSecret + @sessionKey + @appKey
			signature = Digest::MD5.hexdigest(signature)
				
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = { 'ns1:sessionKey' => @sessionKey, 'ns1:signature' => signature }
			end
		
			code = response.to_hash[:authenticate_application_response][:authenticate_application_result][:code].to_i
			if code == 0
				return 0
			else
				return code
			end
		rescue Savon::Error => error
			return -1
		end
	# end private method declarations
	
	public
	# start public method declarations
		def initialize (appKey, appSecret, wsdl = false, version = 2)
			Savon.configure do |config|
				config.log = false				# disable logging
				config.log_level = :error		# changing the log level
				HTTPI.log = false				# Hide HTTPI logging messages
				config.soap_version = version	# Set soap version
												# If version is 1.1, change contentType
				@@h_contentType  = 'text/xml; charset=utf-8; action=' + @@soapAction + '/' if version == 1
			end
			
			@appKey		= appKey
			@appSecret  = appSecret
		
			@client = Savon::Client.new do |wsdl, http|
				http.headers['Host'] 		 = @@h_host
				http.headers['Connection']   = @@h_connection
				http.headers['User-Agent']   = @@h_userAgent
				wsdl.endpoint = @@location
				wsdl.namespace = @@soapAction
				wsdl.document = @@location + '?wsdl' if wsdl == true
			end
				
			code = beginSession
			throwError code unless code == 0
				
			code = authenticateApplication
			throwError code unless code == 0
				
		rescue Savon::Error => error
			puts error.to_s
		end

		def destroy()
			setMethod 'EndSession'
								
			@client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = { 'ns1:sessionKey' => @sessionKey }
			end
		rescue Savon::Error => error
			# what to do? Can't close the session, just return!
		end
		
		def createUser(name, email, password, externalID)
			setMethod 'CreateUser'
			
			throwError(106) if name.blank? || email.blank? || password.blank? || externalID.blank?
			
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' => @sessionKey,
							  'ns1:info' => {
											  'ns1:Name' => name,
											  'ns1:Email' => email,
											  'ns1:Password' => password,
											  'ns1:ExternalID' => externalID
											}
							}
			end

			code = response.to_hash[:create_user_response][:create_user_result][:code].to_i
			if code == 0
				return 0
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end
		
		def getTokenAuthUrl(email, password, externalID)
			setMethod 'GetTokenAuthUrl'
		
			throwError(106) if email.blank? || password.blank? || externalID.blank?
			
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' => @sessionKey,
							  'ns1:info' => {
											  'ns1:Email' => email,
											  'ns1:Password' => password,
											  'ns1:ExternalID' => externalID
											}
							}
			end

			temp = response.to_hash[:get_token_auth_url_response][:get_token_auth_url_result]
			code = temp[:code].to_i
			if code == 0
				return temp[:data][:url]
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end
		
		def getCourse(courseID)
			setMethod 'GetCourse'
		
			throwError(106) if courseID.blank?
			
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' => @sessionKey,
							  'ns1:courseID' => courseID
							}
			end

			temp = response.to_hash[:get_course_response][:get_course_result]
			code = temp[:code].to_i
			if code == 0
				return temp[:data][:course]
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end
		
		def getEquipment(equipmentID)
			setMethod 'GetEquipment'
		
			throwError(106) if equipmentID.blank?
			
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' => @sessionKey,
							  'ns1:equipmentID' => equipmentID
							}
			end

			temp = response.to_hash[:get_equipment_response][:get_equipment_result]
			code = temp[:code].to_i
			if code == 0
				return temp[:data][:equipment]
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end

		def getPreferences(pref = nil)
			return (pref.blank? ? @userPreferences : @userPreferences[pref])
		end
		
		def getWeeklyStats(beginDate, endDate = nil, activity = 0, firstDayOfWeek = 'Monday', fullWeek = true)
			setMethod 'GetWeeklyStats'
		
			beginDate = Date.parse(beginDate)
			beginDate = beginDate.strftime("%Y-%m-%dT%H:%M:%S")

			endDate = (endDate.blank? ? Date.today : Date.parse(endDate))
			endDate = endDate.strftime("%Y-%m-%dT%H:%M:%S")
			
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' => @sessionKey,
							  'ns1:begin' => beginDate,
							  'ns1:end' => endDate,
							  'ns1:activity' => activity,
							  'ns1:firstDayOfWeek' => firstDayOfWeek,
							  'ns1:fullWeek' => fullWeek
							}
			end

			temp = response.to_hash[:get_weekly_stats_response][:get_weekly_stats_result]
			code = temp[:code].to_i
			if code == 0
				return temp[:data][:stats][:stats_data]
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end
		
		def getWorkout(workoutID)
			setMethod 'GetWorkout'
		
			throwError(106) if workoutID.blank?
			
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' => @sessionKey,
							  'ns1:workoutID' => workoutID
							}
			end

			temp = response.to_hash[:get_workout_response][:get_workout_result]
			code = temp[:code].to_i
			if code == 0
				return temp[:data][:workout]
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end
		
		def getWorkoutData(workoutID, type = 'Gpx', encoding = 'UTF8')
			setMethod 'GetWorkoutData'

			throwError(106) if workoutID.blank?
			
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' => @sessionKey,
							  'ns1:workoutID'  => workoutID,
							  'ns1:type' 	   => type,
							  'ns1:encoding'   => encoding
							}
			end
			
			temp = response.to_hash[:get_workout_data_response][:get_workout_data_result]
			code = temp[:code].to_i
			if code == 0
				# Strip out the unwanted XML from the XML response
				return response.to_xml.sub(/<\?xml(.+?)<WorkoutData>/, '').sub(/<\/WorkoutData(.+?)Envelope>/, '')
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end
		
		def listCourses(sortBy = 'ReferenceDate', offset = 0, limit = 100)
			setMethod 'ListCourses'

			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey'	=> @sessionKey,
							  'ns1:filter'		=> {
													'ns1:SortBy' => (sortBy.to_i == 0 ? 'ReferenceDate' : 'Alphabetical')
												   },
							  'ns1:offset'		=> offset,
							  'ns1:limit'   	=> limit
							}
			end
			
			temp = response.to_hash[:list_courses_response][:list_courses_result]
			code = temp[:code].to_i
			if code == 0
				return temp[:data][:courses][:course]
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end
		
		def listEquipment(showRetired = true, eventTypeID = 0, offset = 0, limit = 100)
			setMethod 'ListEquipment'
		
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' => @sessionKey,
							  'ns1:filter'	   =>  {
													 'ns1:showRetired' => showRetired,
													 'ns1:eventTypeID' => eventTypeID
												   },
							  'ns1:offset' 	   => offset,
							  'ns1:limit'   => limit
							}
			end
			
			temp = response.to_hash[:list_equipment_response][:list_equipment_result]
			code = temp[:code].to_i
			if code == 0
				return temp[:data][:equipment][:equipment]
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end
		
		def listEventTypes()
			setMethod 'ListEventTypes'
		
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' => @sessionKey
							}
			end
			
			temp = response.to_hash[:list_event_types_response][:list_event_types_result]
			code = temp[:code].to_i
			if code == 0
				return temp[:data][:event_types][:event_type]
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end
	
		def listWorkouts(fields, offset = 0, limit = 0)
			setMethod 'ListWorkouts'
		
			throwError(106) if fields.blank?
		
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' => @sessionKey,
							  'ns1:filter'	   => {
													'ns1:Fields' => {
																	  'ns1:DataFieldID' => fields
																	}
												  },
							  'ns1:offset'	   => offset,
							  'ns1:limit'	   => limit
							}
			end
			
			temp = response.to_hash[:list_workouts_response][:list_workouts_result]
			code = temp[:code].to_i
			if code == 0
				return temp[:data][:workouts][:workout]
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end
		
		def importWorkouts(data, type = 'Gpx', encoding = 'UTF8')
			setMethod 'ImportWorkouts'
			
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' => @sessionKey,
							  'ns1:type'	   => type,
							  'ns1:encoding'   => encoding,
							  'ns1:data'	   => data
							}
			end

			temp = response.to_hash[:import_workouts_response][:import_workouts_result]
			code = temp[:code].to_i
			if code == 0
				return temp[:data][:workout_ids][:guid]
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end

		def saveCourse(distance, distanceUnit, name, notes = nil, surface = nil)
			setMethod 'SaveCourse'
			
			throwError(106) if !(Numeric === distance) || distanceUnit.blank? || name.blank?
			
			# Allow numericals to be passed
			if Numeric === distance
				case distanceUnit
					when 0
						distanceUnit = 'Kilometer'
					when 1
						distanceUnit = 'Mile'
					when 2
						distanceUnit = 'Meter'
					when 3
						distanceUnit = 'Yard'
					else
						distanceUnit = 'Unknown'
				end
			else
				distanceUnit = distanceUnit.capitalize
			end
		
			surface ||= createSurfaceType
			
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' 	=> @sessionKey,
							  'ns1:course'		=> {
													 'ns1:Name'			=> name,
													 'ns1:Distance'		=> distance,
													 'ns1:DistanceUnit'	=> distanceUnit,
													 'ns1:Notes'		=> notes,
													 'ns1:Surfaces'		=> surface
												   }
							}
			end

			temp = response.to_hash[:save_course_response][:save_course_result]
			code = temp[:code].to_i
			if code == 0
				return temp[:data][:course_id]
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end
		
		def saveEquipment(equipmentID, brandName, brandID, modelName, modelID, serial, eventTypeID, price, initialDistance, distance, purchaseDate, retireDate, notes)
			setMethod 'SaveEquipment'
			
			# Should cover all types of numbers, since all numerical data types inherit from Numeric
			throwError(106) unless Numeric === price && Numeric === eventTypeID
			
			purchaseDate = Date.parse(purchaseDate)
			purchaseDate = purchaseDate.strftime("%Y-%m-%dT%H:%M:%S")

			unless retireDate.blank?
				retireDate = Date.parse(retireDate)
				retireDate = retireDate.strftime("%Y-%m-%dT%H:%M:%S")
			end
	
			equipment = {
						  'ns1:ID' 				=> equipmentID,
						  'ns1:EventTypeID'		=> eventTypeID,
						  'ns1:BrandName'		=> brandName,
						  'ns1:BrandID'			=> brandID,
						  'ns1:ModelName'		=> modelName,
						  'ns1:ModelID'			=> modelID,
						  'ns1:Serial'			=> serial,
						  'ns1:Price'			=> price,
						  'ns1:InitialDistance'	=> initialDistance,
						  'ns1:Distance'		=> distance,
						  'ns1:PurchaseDate'	=> purchaseDate,
						  'ns1:Notes'			=> notes
						}
			
			equipment['ns1:RetireDate']	= retireDate unless retireDate.blank?
	
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' 	=> @sessionKey,
							  'ns1:equipment'	=> equipment
							}
			end

			p response.to_hash
			temp = response.to_hash[:save_equipment_response][:save_equipment_result]
			code = temp[:code].to_i
			if code == 0
				return temp[:data][:equipment]
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end
		
		def savePreferences(distanceUnit = nil, weightUnit = nil, temperatureUnit = nil, firstDayOfWeek = 0)
			setMethod 'SavePreferences'
			
			# Must specify at least one argument
			throwError(106) if distanceUnit.blank? && weightUnit.blank? && temperatureUnit.blank? && firstDayOfWeek.blank?
			
			if distanceUnit.blank?
				distanceUnit = @userPreferences[:distance_unit]
			else
				if Numeric === distanceUnit
					case distanceUnit
						when 0
							distanceUnit = 'Kilometer'
						when 1
							distanceUnit = 'Mile'
						when 2
							distanceUnit = 'Meter'
						when 3
							distanceUnit = 'Yard'
						else
							distanceUnit = 'Unknown'
					end
				else
					distanceUnit = distanceUnit.capitalize
				end
				@userPreferences[:distance_unit] = distanceUnit
			end
		
			if weightUnit.blank?
				weightUnit = @userPreferences[:weight_unit]
			else
				if Numeric === weightUnit
					case weightUnit
						when 0
							weightUnit = 'Pound'
						when 1
							weightUnit = 'Kilogram'
						else
							weightUnit = 'Unknown'
					end
				else
					weightUnit = weightUnit.capitalize
				end
				@userPreferences[:weight_unit] = weightUnit
			end
			
			if temperatureUnit.blank?
				temperatureUnit = @userPreferences[:temperature_unit]
			else
				if Numeric === temperatureUnit
					case temperatureUnit
						when 0
							temperatureUnit = 'Celsius'
						when 1
							temperatureUnit = 'Fahrenheit'
						else
							temperatureUnit = 'Unknown'
					end
				else
					temperatureUnit = temperatureUnit.capitalize
				end
				@userPreferences[:temperature_unit] = temperatureUnit
			end

			if firstDayOfWeek.blank?
				firstDayOfWeek = @userPreferences[:first_day_of_week]
			else
				if Numeric === firstDayOfWeek
					case firstDayOfWeek
						when 0
							firstDayOfWeek = 'Monday'
						when 7
							firstDayOfWeek = 'Sunday'
						else
							firstDayOfWeek = 'Monday'
					end
				else
					firstDayOfWeek = firstDayOfWeek.capitalize
				end
				@userPreferences[:first_day_of_week] = firstDayOfWeek
			end
		
			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' 	=> @sessionKey,
							  'ns1:preferences'	=> {
													 'ns1:DistanceUnit'		=> distanceUnit,
													 'ns1:WeightUnit'		=> weightUnit,
													 'ns1:TemperatureUnit'	=> temperatureUnit,
													 'ns1:FirstDayOfWeek'	=> firstDayOfWeek
												   }
							}
			end

			temp = response.to_hash[:save_preferences_response][:save_preferences_result]
			code = temp[:code].to_i
			if code == 0
				return 0
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end
		
		def saveWorkout(workout)
			setMethod 'SaveWorkout'
			
			throwError(106) unless Workout === workout && !workout.blank?
			
		
			aWorkout = {}
		
			aWorkout['ns1:ID'] = workout.workoutID unless workout.workoutID.blank?
			aWorkout['ns1:Date'] = (workout.date.blank? ? Date.today : Date.parse(workout.date))
			aWorkout['ns1:Date'] = aWorkout['ns1:Date'].strftime("%Y-%m-%d")
			aWorkout['ns1:timeOfDay'] = workout.timeOfDay unless workout.timeOfDay.blank?
			aWorkout['ns1:EventType'] = workout.eventType
			aWorkout['ns1:EventTypeID'] = workout.eventTypeID
			aWorkout['ns1:EventSubType'] = workout.eventSubType
			aWorkout['ns1:EventSubTypeID'] = workout.eventSubTypeID
			aWorkout['ns1:courseName'] = workout.courseName unless workout.courseName.blank?
			aWorkout['ns1:courseID'] = workout.courseID unless workout.courseID.blank?
			aWorkout['ns1:Distance'] = workout.distance unless workout.distance.blank?
		
			unless (workout.distance.blank? && workout.distanceUnit.blank?)
				if Numeric === workout.distanceUnit
					case workout.distanceUnit
						when 0
							aWorkout['ns1:DistanceUnit'] = 'Kilometer'
						when 1
							aWorkout['ns1:DistanceUnit'] = 'Mile'
						when 2
							aWorkout['ns1:DistanceUnit'] = 'Meter'
						when 3
							aWorkout['ns1:DistanceUnit'] = 'Yard'
						else
							aWorkout['ns1:DistanceUnit'] = 'Unknown'
					end
				else
					aWorkout['ns1:DistanceUnit'] = workout.distanceUnit.capitalize
				end
			end
				
			aWorkout['ns1:Duration'] = workout.duration unless workout.duration.blank?
			aWorkout['ns1:EquipmentName'] = workout.equipmentName unless workout.equipmentName.blank?
			aWorkout['ns1:EquipmentID'] = workout.equipmentID unless workout.equipmentID.blank?
			aWorkout['ns1:Weight'] = workout.weight unless workout.weight.blank?

			unless (workout.weight.blank? && workout.weightUnit.blank?)
				if Numeric === workout.weightUnit
					case workout.weightUnit
						when 0
							aWorkout['ns1:WeightUnit'] = 'Pound'
						when 1
							aWorkout['ns1:WeightUnit'] = 'Kilogram'
						else
							aWorkout['ns1:WeightUnit'] = 'Unknown'
					end
				else
					aWorkout['ns1:WeightUnit'] = workout.weightUnit.capitalize
				end
			end
		
			aWorkout['ns1:RestHR'] = workout.restHR unless workout.restHR.blank?
			aWorkout['ns1:AvgHR'] = workout.avgHR unless workout.avgHR.blank?
			aWorkout['ns1:MaxHR'] = workout.maxHR unless workout.maxHR.blank?
			aWorkout['ns1:Temperature'] = workout.temperature unless workout.temperature.blank?
		
			unless (workout.temperature.blank? && workout.temperatureUnit.blank?)
				if Numeric === workout.temperatureUnit
					case workout.temperatureUnit
						when 0
							aWorkout['ns1:TemperatureUnit'] = 'Celsius'
						when 1
							aWorkout['ns1:TemperatureUnit'] = 'Fahrenheit'
						else
							aWorkout['ns1:TemperatureUnit'] = 'Unknown'
					end
				else
					aWorkout['ns1:TemperatureUnit'] = workout.temperatureUnit.capitalize
				end
			end
		
			aWorkout['ns1:Notes'] = workout.notes unless workout.notes.blank?		
			aWorkout['ns1:WeatherConditions'] = workout.weatherConditions unless workout.weatherConditions.blank?
			aWorkout['ns1:FieldPlacement'] = workout.fieldPlacement unless workout.fieldPlacement.blank?
			aWorkout['ns1:FieldSize'] = workout.fieldSize unless workout.fieldSize.blank?
			aWorkout['ns1:GroupMinAge'] = workout.groupMinAge unless workout.groupMinAge.blank?
			aWorkout['ns1:GroupMaxAge'] = workout.groupMaxAge unless workout.groupMaxAge.blank?
			aWorkout['ns1:GroupPlacement'] = workout.groupPlacement unless workout.groupPlacement.blank?
			aWorkout['ns1:GroupSize'] = workout.groupSize unless workout.groupSize.blank?
			aWorkout['ns1:GenderPlacement'] = workout.genderPlacement unless workout.genderPlacement.blank?
			aWorkout['ns1:GenderSize'] = workout.genderSize unless workout.genderSize.blank?
			aWorkout['ns1:Quality'] = workout.quality unless workout.quality.blank?
			aWorkout['ns1:Effort'] = workout.effort unless workout.effort.blank?
			aWorkout['ns1:Url'] = workout.url unless workout.url.blank?
			aWorkout['ns1:IntervalSet'] = workout.intervalSet unless workout.intervalSet.blank?
			aWorkout['ns1:HasDetailedData'] = (workout.intervalSet.blank? ? false : true)

			response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
				soap.input = 'ns1:' + @method
				soap.body = {
							  'ns1:sessionKey' 	=> @sessionKey,
							  'ns1:workout'		=> aWorkout
							}
			end

			temp = response.to_hash[:save_workout_response][:save_workout_result]
			code = temp[:code].to_i
			if code == 0
				return temp[:data][:workout_id]
			else
				throwError code
			end
		rescue Savon::Error => error
			return -1
		end

		def impersonateUser(email, password)
			begin
				setMethod 'ImpersonateUser'
		
				signature = @appSecret + @sessionKey + email + password
				signature = Digest::MD5.hexdigest(signature)

				response = @client.request 'ns1', @@soapAction + '/' + @method do |soap|
					soap.input = 'ns1:' + @method
					soap.body = {
								  'ns1:sessionKey' => @sessionKey,
								  'ns1:email' => email,
								  'ns1:password' => password,
								  'ns1:signature' => signature
								}
				end
				
				temp = response.to_hash[:impersonate_user_response][:impersonate_user_result]
				code = temp[:code].to_i
				if code == 0
					@userPreferences = temp[:data][:user_preferences]
					return 0
				else
					throwError code
				end
			rescue Savon::Error => error
				return -1
			end
		end
		
		def createSurfaceType(asphalt = false, concrete = false, grass = false, trail = false, track = false, treadmill = false)
			return {
					 'Asphalt' => asphalt,
					 'Concrete' => concrete,
					 'Grass' => grass,
					 'Trail' => trail,
					 'Track' => track,
					 'Treadmill' => treadmill
				   }
		end
		
		def getUserPrefs()
			puts @userPreferences
		end
	# end public method declarations
end